Dynamic Object Language Labs Presents





A new, dynamic, object oriented programming language


Yolambda - A Conceptual Overview

Yolambda is a new dynamic object oriented language. Dynamic languages have a long and rich history among programming languages, and include such languages as Smalltalk and Basic. Yolambda is object-based from its roots, and incorporates the most modern ideas of compilation, flexible typing, and support for object oriented operating system extensions. Although Yolambda is a relatively easy language to learn and use (as are most dynamic languages) it is primarily a tool for the serious programmer.

This page is intended to be a brief introduction to the Yolambda language, focusing on key concepts and design principles, rather than giving a detailed description of the language. For more detailed description and instruction in the use of the language, you should consult the Yolambda Reference Manual and the Yolambda User Guide.

Design Principles

Yolambda is a language based on the straightforward general implementation of a few simple but powerful concepts. The language is simple because it is uncluttered with exception cases. It is small because it is general enough to permit straightforward extension.

Yolambda doesn't try to provide everything that any potential user of the language might ever conceivably want, but rather to provide the basic capabilities that the majority of users will need, and a powerful framework for extension. By keeping the language unencumbered with complexity, the language remains accessible to beginners and sophisticated users alike.

Two aspects of the language are central and crucial, they are the very reasons for the language's existence. They are that the language is object oriented, and dynamic. What we mean by object orientation is that everything manipulated by the language, including code and data, are objects. Classes form a directed graph, and behaviors and structure of parent classes are inherited by their children. This capability is called inheritance, and it is a powerful programming tool. It means that code applicable to a wide class of objects can be written for that entire class, and other code designed for a small subset of the larger class of objects, can be written specifically for the appropriate subclass. This is a powerful organizational aid which supports the ability to write modular, reusable software. Another aspect of object oriented code is that similar or identical procedures that nevertheless are implemented in subtly different ways depending on the objects that they are operating on, can have the same name. This simplifying capability, called polymorphism, is tremendously important to making programming systems less complex and thus easier to build and maintain, for a given level of functionality. So object orientation means that objects are ubiquitous, and that the language support multiple inheritance and polymorphism. (Some computer scientists believe that object orientation also requires encapsulation, the capability of building barriers between bodies of code. The main idea is that some parts of code need to be distinguished as implementation, and hidden, while other parts are interface, and need to be published. We believe that encapsulation is an important idea but orthogonal to the idea of object orientation. It has mistakenly been associated with object orientation because of the design and implementation of some early object oriented programming languages.)

The concept of dynamism in programming languages refers to the ability to write and immediately execute code, both in independent chunks, and as part of a larger system. This capability supports exploratory programming and prototyping, as well as being an important aid to debugging. Another aspect of dynamism is runtime type checking. The ability to determine the type of an object at runtime, and dispatch code based on an objects' type at runtime, is an important flexible capability. This capability for running code to be self aware is tremendously useful during development and debugging. It means that the programming system can better aid in discovering the cause of errors and in recovering from them. It also makes possible recovery mechanisms that can operate during application delivery, to provide added robustness.

A third design feature, somewhat less central than the first two, but very important nevertheless, is the concept that all of the entities that the programmer can access can be operated on without restriction. That is, that all of the entities with which the programmer deals are first class entities. In many programming languages, there are objects that can be accessed but not manipulated, using the standard constructs of the language. In Yolambda, all entities that can be accessed are objects, and all of them are first class. Of particular importance is having First Class Procedures. Yolambda is a procedural language. Work gets done in a Yolambda program by applying procedures. Procedures in Yolambda are first class, which means that they can be manipulated within a program, as values in much the same way that numbers or symbols can. The lifetime of a procedure is not governed by arbitrary and complex implementation considerations and the result is a language that is at once simple, extensible and powerful.

Yolambda Design Principles

Listed below are some additional design principles that we have tried to follow, which are, in comparison to object orientation and dynamism, metafeatures of the language.

Simple:
When we say that the language is simple, we mean that it has few arbitrary rules and restrictions. It is based on a very straightforward model. This kind of simplicity makes the language easy to learn and use, and at the same time makes the language powerful. First class procedures and the object oriented model, both discussed above, are examples of applications of the principle of simplicity.
Small:
Yolambda is small in a number of important ways. First, it attempts to separate language from library in a way that keeps the core language as small as possible. Second, the language itself is based on a simple clean model, so there are much less restrictive aspects to the language. In most languages, as much of the complexity derives from the restrictions imposed in order to accommodate a particular implementation strategy, as from the language itself.
Open:
Yolambda is designed to be an open language. By open, we refer to the orientation of the language to straightforward interaction with the world in which it operates. A program is not an island! A program must interact with the operating environment upon which it runs. The operating system, network, file system, and other applications are all components of the environment. Good modularity dictates that the act of interfacing with an external entity be separated from the program that is doing the interaction. Through the use of a meta object approach, in Yolambda, it is possible to internalize the interfaces to the environment, so that they become first class parts of the Yolambda class system.
Orthogonal:
The principle of orthogonality is a significant one in Yolambda since the generality of the mechanisms is in the limiting case, very great. The principle of orthogonality, simply stated, is that a programmer will only pay the price for a general feature when he uses it, and that its presence in the language will not cause all of his programs to run slower. Languages that utilize libraries exhibit orthogonality, in that if I don't use the fast fourier transform library procedure, I will not pay the price of it. The price of a feature can be measured along many dimensions, but the most significant are speed and size.
Efficient:
A specific goal of the language has been to provide efficient emitted code. Where possible, this has been achieved by compiler smarts. Because of the generality inherent in Yolambda, it is possible to write very inefficient programs. In a sense, by not placing restrictions on the language, the programmer is given the freedom to choose a performance point.
Incremental Development:
Incremental development is foundational concept in Yolambda. Programs are developed incrementally, and both the language and the tools are designed to promote this style of development. Polymorphic procedures are used to help disentangle incrementally developed pieces of a procedure. Rather than having the constituent parts of a procedure be combined into a single textual representation, in Yolambda, a procedure is defined as the combination of separately written parts.
Code Reusability:
The final principle is that of providing for code reusability. Software is generally the source of some of mankind's most complex constructions. Consider operating systems, and huge application systems, often involving millions of lines of code. No mechanical or chemical constructs involve so many separately manipulated components. Because the production of software is so difficult, it is absolutely essential that well developed pieces of software, representing significant human investment, be amortized over several uses. In Yolambda, code reusability derives from our particular object oriented model, from the simplicity of the design, from first class procedures, and from orthogonal design.

Basic Concepts

We start with the concept of an object. An object is an abstract or concrete thing; as abstract as the number 33, or as concrete as the string "33". All the things we familiarly manipulate in our programming tasks are objects, e.g. structures, procedures, symbols, vectors, and lists.

Next we present the concept of class. Classes establish a taxonomy for objects. We group all the possible objects into sets or classes, where all the objects that belong to the same class are of the same type, in computer science parlance. Classes can be specialized. If, for example, we consider the class of numbers, we can distinguish the subclass of real numbers, and further distinguish integers as a subclass of the reals. Subclasses inherit structure and type from their parents.

Among the objects that we deal with in Yolambda are expressions and procedures. Any construct in the language, either atomic like a symbol, or compound, like a list, is a form. Expressions are forms that return values. Procedures are parameterized program fragments made up of one or more expressions. Because expressions and procedures are objects that can be the value of a variable, passed to a procedure as an argument or returned from a procedure, we say that they are first class objects.

One of the most important types of expressions in Yolambda is the lambda expression. A lambda expression establishes a local environment, and specifies a body of expressions that compute a value in the context of that environment. All of the derived binding constructs in Yolambda are defined in terms of lambda expressions, as are procedures, so an understanding of the semantics of lambda expressions will be crucial to an understanding of the semantics of Yolambda.

Like classes, Yolambda procedures can also be specialized. Each of the parameters to a procedure can be specialized to accept arguments only of a specific class. Such a specialized procedure is called a method, and the collection of methods along with the algorithm for determining which methods are to be applied in any given procedure call, are called a polymorphic procedure. The combination of specialized classes and specialized procedures provides the uniquely useful modularity discipline known as object oriented programming.

A Yolambda program is a sequence of imperative expressions. In practice, a Yolambda program is a sequence of binding expressions, which assign values to variables, followed by a sequence of procedure calls, whether named or anonymous. The bindings are basically definitions, which give data values to some variables and procedural values to others. The procedure call expressions are either lists whose first element is a procedure name, or lambda expressions. List structure is uniformly used to represent programs in Yolambda.

The Yolambda Class System

Classes carry all the type information for Yolambda objects. In Yolambda, classes provide the classification of objects into what in other languages is often called the "type" of the object. Several native procedures allow you to define classes and get access to information about classes and objects.

One of the powerful features of class systems is inheritance of structure and properties. When a class is defined, we define the classes from which it inherits. These specified classes are the direct superclasses of the defined class. The order in which those direct superclasses is listed is important; it is called the local precedence order. Besides direct superclasses, classes inherit from indirect superclasses, which are the parents of the direct superclasses (because the superclass property is transitive). There is a total ordering on the set of superclasses for the class, called the precedence order. This ordering controls inheritance for the class, that is, from which superclasses the class inherits its properties. This total ordering will always be consistent with the local precedence order over the direct superclasses, that we described above.

The class system forms a directed graph (because of multiple inheritance, it is not simply a tree). We impose a total ordering on the superclasses of a class in support of resolving inheritance conflicts. This total ordering is called the class precedence list, and if we restrict ourselves to looking at the first superclass of each class, as defined by the class precedence list, then the class system can be viewed as a tree, with class at the root. Within the class of class are standard class and primitive class. Here is the class tree, down to the level of the children of standard class:

Standard Class

Instances of a class have storage associated with them. Instance variables, or slots, are the internal storage for instances of a class. Slots are used to store information about the instance, or information that will need to be used by procedures that manipulate this class of instance. One of the ways that classes can be specialized, is by adding slots. A subclass of a class will have all the slots that the original class has, but may in addition have other slots. So, for example, the class Food could have instances that have slots for shape, taste, and calories. A subclass of Food might be Apple, with all the same slots as food, but also having slots for appleVariety and sugarContent. We say that Apple specializes Food, and inherits from Food.

A class definition indicates the classes from which the defined class will inherit. We can create classes that inherit from several independent classes, which allows us to define classes in usefully orthogonal ways. Apple can inherit from the Fruit class (in the botanical sense) as well as the Food class.

Procedures in Yolambda are written to be specialized for application to certain kinds of objects. For example, we might have a polymorphic procedure, eat. Polymorphic means that the same name is used for several procedures that are related in action but are applicable to different classes of objects. So, we might have one version of eat that applies to instances of the class of people and instances of the class of Food. Another version of eat could apply to People and to Apples. A third version could apply to instances of the class of Pig, and Apples. The different procedure versions are called methods. Since an apple is a kind of food, we say that Apple is a subclass of Food. Therefore, the eat method for People and Food also applies to People and Apples. This is a second form of inheritance and specialization in the class system.

A concept of great significance to using the class system is the concept of a protocol. A protocol is a collection of behaviors which can be said to define an interface. We can think of eating as an interface protocol between consumers and food. That protocol would have the behaviors: acquisition, preparation, consumption and cleanup as required, constituent behaviors of the protocol. If the consumer class in question is Dog, then cleaning and cooking will not be part of the preparation methods, but they will be for the consumer class of People.

Metaclasses in Yolambda

The Yolambda class system is the core of the language. Every object is an instances of at least one class. By instance, we mean essentially the same thing that is meant intuitively by saying that an object is a member of a set. Classes are also objects, and hence they too must belong to a class. We call the class that a class is the member of a metaclass. The meaning of "meta" being used here is that of being beyond. A metaclass bears to class the relationship that a class bears to its instances. Each kind of instance has two separate important groups of properties. One group are the properties associated with the type of the object, and the component values of the object. In Yolambda, these aspects of the instance are controlled and initialized by the class. The other group of properties are related to the storage requirements of the instance. Issues of storage implementation and organization are the responsibility of the metaclass.

The careful separation of responsibilities between class and metaclass gives Yolambda an unusual and powerful reflective capability concerning its object oriented functionality. This reflective capability means that Yolambda can provide information about and manipulate its own implementation. For this reason it is relatively easy to implement in Yolambda object systems (or interfaces to object systems) that have different policies and procedures from the ones used to implement the Yolambda class system. This capability is one of the reasons that Yolambda is so powerful for interfacing with foreign object systems.

Collection Classes

Collections are objects which have other objects as components or elements. Collections may be ordered or unordered, may allow multiple copies of the same object, or not, may be accessed randomly or only serially. All types of collections are subclasses of the collection class, and inherit certain attributes from that class. Collections are very important in programming, because they provide the ability to manipulate and define large quantities of data. Thus we often need to operate on data in the form of collections. Of particular importance is the ability to iterate over the elements of a collection, performing some operation (possibly conditional) on each element. Yolambda makes it easy to define and perform such iterations.

The basic collection protocol includes the procedures that all instantiable collection classes must support. It includes the ability to add and remove elements from a collection, the ability to test a collection for the occurrence of a particular object, the ability to determine the size of the collection, and at least one iteration form for iterating over the elements of the collection. All collections also support the iteration protocol, which we describe in a later section.

The collection classes include:

Bag
A Bag is an unordered collection of things. Identical elements may have multiple occurrences in a Bag. Bags have no external indexing methods, but like any collection, one can use the iteration procedures for or loop to iterate over the elements of a Bag.
Set
The class Set is similar to the class Bag. Like Bags, they have no indexing methods, but unlike Bags, Sets are limited to a single occurrence of each element. The elements of a Set are called members.
Dictionary
Dictionaries are also unordered collections. The dictionary stores associations, which are key/value pairs. The key serves as a name for the value, and typical use of a dictionary is to search for the name, in order to retrieve the associated value. The key/value pair can be stored as an actual pair, as in an Association List, or they can be stored using some form of tabular entry, such as a Hash Table.
SequenceableCollection
These collection classes are discussed in a separate section, below.

Sequenceable Collections

The Class Tree for SequenceableCollection:

Collection

The Sequenceable Collections are an important subclass of Collections, because the ability to have indexed or ordered storage that can be easily searched and accessed is extremely useful to programmers. Subclasses of SequenceableCollection include strings, vectors, arrays and linked lists.

Sorted Collections are ordered by a sorting algorithm. The type of storage and indexing is irrelevant to these collections, what is important is the procedure that determines the order of the elements of an instance of this class.

Ordered Collections are ordered by some non-sorting algorithm or policy. Stacks and Queues are examples of class OrderedCollection. As with SortedCollection, the type of storage and indexing is irrelevant to these collections, what is important is the procedure that determines the order of the elements of an instance of this class.

The Arrayed Collection class is characterized by sequences indexed by a fixed range of integers. Subclasses include Vector and String. These classes are among the most useful to programmers. In most languages, there are special procedures designed to operate on these separate useful collection classes. This results in unnecessary duplication, and places an additional burden on the programmer's memory. In Yolambda, a uniform set of procedures applies to all of the Sequenceable Collections making it very easy to both operate on instances of these classes, and to modify programs to utilize different storage constraints. This same feature applies to the Linked List class, which is characterized by sequences ordered by their position in a chain of Links.

Intervals are among the more useful SequenceableCollections. An Interval is a range of integers with an integral step. The elements of the interval are computed, rather than stored, so elements of an Interval cannot be removed, stored or added. Intervals are very useful in defining iterations that should be executed a fixed number of times, or over a subset of the elements of a vector or list, for example.

The SequenceableCollection protocol includes procedures for filling a collection, accessing the first and last elements of a collection, and accessing and setting the value of an indexed location in the collection.

Iteration Protocol

The purpose of the iteration protocol is to allow developers of collection classes to have a low level iteration protocol to implement for their collection. Higher level iteration constructs, such as for, implemented in terms of this low level protocol will then automatically apply to the new collection.

The iteration protocol is the protocol for traversing a collection, in order to perform some test or operation on the elements of the collection. The protocol is based on the idea of the state of iteration. One element of a collection is associated with each state, and the protocol is composed of procedures for obtaining an initial state, determining the final state, determining the next state given the current state, and accessing the collection element corresponding to the current state. An additional procedure in the protocol allows the copying of a state, in order to support a backtracking capability.

Condition System

Yolambda's condition system supports Yolambda's treatment of exceptions, that is, events that are considered to be outside the normal scope of operations of procedures, programs and systems. Exceptions can be lack of availability of a device that is expected to be available, incorrect data type as an argument to a procedure, floating point under or over flow during a computation, or an attempt to get a value from an unbound variable. We all know that removing bugs from code makes the code more solid and robust. However, even with most or all bugs removed, a user or operating system or hardware error can cause our programs to fail. The ability to handle exception conditions gracefully adds significantly to both the perception and actuality of robustness in software.

Let's consider an example. Suppose we have some code which makes a number of floating point computations. The hardware/operating system detects a floating point overflow. The overflow is an interesting event, an exception, which will affect our computation. First, the overflow condition must be signalled or raised. This really means leaving or stopping the context of the current computation so that the exception can be dealt with. In general, the code that raises the exception need know nothing of which code will deal with the exception, or how. We then need some code which attempts to do something with the notice of the event. The sorts of things that we can do are to abort the computation, abort the program, request help from a user or operator, or start the computation over again with different arguments (that presumably won't cause overflow), or with a different algorithm (which also promises to reduce the likelihood of overflow).

The four main concepts that make up Yolambda's condition system are conditions (for exception conditions), signals, handlers (condition handlers), and restarts. In our example, the overflow exception was the condition. The first things that we needed to do with it was leave the current computation, and raise, or signal the condition. Next, we need to have one or more condition handlers to volunteer to do something with the exception. Finally, it is often advantageous to have several restart points available, for starting a portion of the computation over, in order to accomplish the overall goal of the program.

Conditions are the objects that are used to represent the exceptional events that programmers may want to account for, and which the Yolambda system may be obligated to report. Signalling is the concept of raising the exception, by reporting the condition object, perhaps at successive levels of the program or system. Handling is the concept of providing, at one or more levels of the program or system, code or procedures designed to recover, restart, or take alternative action in response to an exception. Handling a condition generally involves a transfer of control, and restarts are a way of defining specific, useful targets for transfer of control.

References

Harold Abelson and Gerald Jay Sussman with Julie Sussman. Structure and Interpretation of Computer Programs. MIT Press, Cambridge, 1985.

Daniel G. Bobrow, Linda D. DiMichiel, Richard P. Gabriel, Sonya E. Keene, Gregor Kiczales, and David Moon. Common Lisp Object Specification: X3J13 Document 88-002R. SIGPLAN Notices 23 September 1988.

Adele Goldberg and David Robson. Smalltalk-80: The Language and its Implementation. Addison-Wesley, Reading, 1983.

IEEE Standard for the Scheme Programming Language, IEEE Standard 1178-1990, IEEE Piscataway, 1991.

Sonya E. Keene. Object Oriented Programming in Common Lisp: A programmer's Guide to CLOS. Addison-Wesley, Reading, 1989.

Paul Robertson. On Reflection and Refraction. In Reflection and Meta-Level Architecture, Proceedings of the 1992 International Workshop on New Models for Software Architecture, Edited by Akimori Yonezawa and Brian C. Smith, Tokyo, 1992.

Andrew Shalit. Dylan: An Object Oriented Dynamic Language. Apple, Cupertino, 1992.

Guy Lewis Steele Jr. and Gerald Jay Sussman. The Revised Report on SCHEME: A Dialect of Lisp. AI Memo 452. MIT Artificial Intelligence Laboratory, Cambridge, 1978.

Guy Lewis Steele Jr. Common Lisp: The Language, Second Edition. Digital, Bedford, 1990.


Copyright © 1995 DOLL Inc. All Rights Reserved.

Back